/*
* Copyright (C) 2016 MediaTek Inc.
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/

/*
** Id: //Department/DaVinci/BRANCHES/MT6620_WIFI_DRIVER_V2_3/nic/cmd_buf.c#1
*/

/*
 * ! \file   "cmd_buf.c"
 *  \brief  This file contain the management function of internal Command Buffer
 *    for CMD_INFO_T.
 *
 *    We'll convert the OID into Command Packet and then send to FW. Thus we need
 *  to copy the OID information to Command Buffer for following reasons.
 *  1. The data structure of OID information may not equal to the data structure of
 *     Command, we cannot use the OID buffer directly.
 *  2. If the Command was not generated by driver we also need a place to store the
 *     information.
 *  3. Because the CMD is NOT FIFO when doing memory allocation (CMD will be generated
 *     from OID or interrupt handler), thus we'll use the Block style of Memory Allocation
 *     here.
 */

/*******************************************************************************
*                         C O M P I L E R   F L A G S
********************************************************************************
*/

/*******************************************************************************
*                    E X T E R N A L   R E F E R E N C E S
********************************************************************************
*/
#include "precomp.h"

/*******************************************************************************
*                              C O N S T A N T S
********************************************************************************
*/

/*******************************************************************************
*                             D A T A   T Y P E S
********************************************************************************
*/

/*******************************************************************************
*                            P U B L I C   D A T A
********************************************************************************
*/

/*******************************************************************************
*                           P R I V A T E   D A T A
********************************************************************************
*/
BOOLEAN fgCmdDumpIsDone = FALSE;
/*******************************************************************************
*                                 M A C R O S
********************************************************************************
*/

/*******************************************************************************
*                   F U N C T I O N   D E C L A R A T I O N S
********************************************************************************
*/

/*******************************************************************************
*                              F U N C T I O N S
********************************************************************************
*/
/*----------------------------------------------------------------------------*/
/*!
* @brief This function is used to initial the MGMT memory pool for CMD Packet.
*
* @param prAdapter  Pointer to the Adapter structure.
*
* @return (none)
*/
/*----------------------------------------------------------------------------*/
VOID cmdBufInitialize(IN P_ADAPTER_T prAdapter)
{
	P_CMD_INFO_T prCmdInfo;
	UINT_32 i;

	ASSERT(prAdapter);

	QUEUE_INITIALIZE(&prAdapter->rFreeCmdList);

	for (i = 0; i < CFG_TX_MAX_CMD_PKT_NUM; i++) {
		prCmdInfo = &prAdapter->arHifCmdDesc[i];
		QUEUE_INSERT_TAIL(&prAdapter->rFreeCmdList, &prCmdInfo->rQueEntry);
	}
	fgCmdDumpIsDone = FALSE;
}				/* end of cmdBufInitialize() */

/*----------------------------------------------------------------------------*/
/*!
* @brief dump CMD queue and print to trace, for debug use only
* @param[in] prQueue	Pointer to the command Queue to be dumped
* @param[in] quename	Name of the queue
*/
/*----------------------------------------------------------------------------*/
VOID cmdBufDumpCmdQueue(P_QUE_T prQueue, CHAR *queName)
{
	P_CMD_INFO_T prCmdInfo = (P_CMD_INFO_T)QUEUE_GET_HEAD(prQueue);

	DBGLOG(NIC, INFO, "Dump CMD info for %s, Elem number:%u\n", queName, prQueue->u4NumElem);
	while (prCmdInfo) {
		P_CMD_INFO_T prCmdInfo1, prCmdInfo2, prCmdInfo3;

		prCmdInfo1 = (P_CMD_INFO_T)QUEUE_GET_NEXT_ENTRY((P_QUE_ENTRY_T)prCmdInfo);
		if (!prCmdInfo1) {
			DBGLOG(NIC, INFO, "CID:%d SEQ:%d\n", prCmdInfo->ucCID, prCmdInfo->ucCmdSeqNum);
			break;
		}
		prCmdInfo2 = (P_CMD_INFO_T)QUEUE_GET_NEXT_ENTRY((P_QUE_ENTRY_T)prCmdInfo1);
		if (!prCmdInfo2) {
			DBGLOG(NIC, INFO, "CID:%d, SEQ:%d; CID:%d, SEQ:%d\n", prCmdInfo->ucCID,
				prCmdInfo->ucCmdSeqNum, prCmdInfo1->ucCID, prCmdInfo1->ucCmdSeqNum);
			break;
		}
		prCmdInfo3 = (P_CMD_INFO_T)QUEUE_GET_NEXT_ENTRY((P_QUE_ENTRY_T)prCmdInfo2);
		if (!prCmdInfo3) {
			DBGLOG(NIC, INFO, "CID:%d, SEQ:%d; CID:%d, SEQ:%d; CID:%d, SEQ:%d\n", prCmdInfo->ucCID,
				prCmdInfo->ucCmdSeqNum, prCmdInfo1->ucCID, prCmdInfo1->ucCmdSeqNum,
				prCmdInfo2->ucCID, prCmdInfo2->ucCmdSeqNum);
			break;
		}
		DBGLOG(NIC, INFO, "CID:%d, SEQ:%d; CID:%d, SEQ:%d; CID:%d, SEQ:%d; CID:%d, SEQ:%d\n",
				prCmdInfo->ucCID, prCmdInfo->ucCmdSeqNum, prCmdInfo1->ucCID,
				prCmdInfo1->ucCmdSeqNum, prCmdInfo2->ucCID, prCmdInfo2->ucCmdSeqNum,
				prCmdInfo3->ucCID, prCmdInfo3->ucCmdSeqNum);
		prCmdInfo = (P_CMD_INFO_T)QUEUE_GET_NEXT_ENTRY((P_QUE_ENTRY_T)prCmdInfo3);
	}
}

/*----------------------------------------------------------------------------*/
/*!
* @brief Allocate CMD_INFO_T from a free list and MGMT memory pool.
*
* @param[in] prAdapter      Pointer to the Adapter structure.
* @param[in] u4Length       Length of the frame buffer to allocate.
*
* @retval NULL      Pointer to the valid CMD Packet handler
* @retval !NULL     Fail to allocat CMD Packet
*/
/*----------------------------------------------------------------------------*/
#if CFG_DBG_MGT_BUF
P_CMD_INFO_T cmdBufAllocateCmdInfoX(IN P_ADAPTER_T prAdapter, IN UINT_32 u4Length, PUINT_8 fileAndLine)
#else
P_CMD_INFO_T cmdBufAllocateCmdInfo(IN P_ADAPTER_T prAdapter, IN UINT_32 u4Length)
#endif
{
	P_CMD_INFO_T prCmdInfo;

	KAL_SPIN_LOCK_DECLARATION();

	DEBUGFUNC("cmdBufAllocateCmdInfo");

	ASSERT(prAdapter);

	KAL_ACQUIRE_SPIN_LOCK(prAdapter, SPIN_LOCK_CMD_RESOURCE);
	QUEUE_REMOVE_HEAD(&prAdapter->rFreeCmdList, prCmdInfo, P_CMD_INFO_T);
	KAL_RELEASE_SPIN_LOCK(prAdapter, SPIN_LOCK_CMD_RESOURCE);

	if (prCmdInfo) {
		/* Setup initial value in CMD_INFO_T */
		prCmdInfo->u2InfoBufLen = 0;
		prCmdInfo->fgIsOid = FALSE;
		prCmdInfo->fgDriverDomainMCR = FALSE;

		if (u4Length) {
			/* Start address of allocated memory */
#if CFG_DBG_MGT_BUF
			prCmdInfo->pucInfoBuffer = cnmMemAllocX(prAdapter, RAM_TYPE_BUF, u4Length, fileAndLine);
#else
			prCmdInfo->pucInfoBuffer = cnmMemAlloc(prAdapter, RAM_TYPE_BUF, u4Length);
#endif

			if (prCmdInfo->pucInfoBuffer == NULL) {
				KAL_ACQUIRE_SPIN_LOCK(prAdapter, SPIN_LOCK_CMD_RESOURCE);
				QUEUE_INSERT_TAIL(&prAdapter->rFreeCmdList, &prCmdInfo->rQueEntry);
				KAL_RELEASE_SPIN_LOCK(prAdapter, SPIN_LOCK_CMD_RESOURCE);

				prCmdInfo = NULL;
			}
		} else {
			prCmdInfo->pucInfoBuffer = NULL;
		}
		fgCmdDumpIsDone = FALSE;
	} else if (!fgCmdDumpIsDone) {
		P_GLUE_INFO_T prGlueInfo = prAdapter->prGlueInfo;
		P_QUE_T prCmdQue = &prGlueInfo->rCmdQueue;
		P_QUE_T prPendingCmdQue = &prAdapter->rPendingCmdQueue;
		P_QUE_T prCmdTxQue = &prAdapter->rTxCmdQueue;
		P_TX_TCQ_STATUS_T prTc = &prAdapter->rTxCtrl.rTc;

		fgCmdDumpIsDone = TRUE;
		cmdBufDumpCmdQueue(prCmdQue, "waiting Tx CMD queue");
		cmdBufDumpCmdQueue(prPendingCmdQue, "waiting response CMD queue");
		cmdBufDumpCmdQueue(prCmdTxQue, "waiting Txing to hif queue");
		if (prCmdQue->u4NumElem + prPendingCmdQue->u4NumElem +
		    prCmdTxQue->u4NumElem + prAdapter->rFreeCmdList.u4NumElem < CFG_TX_MAX_CMD_PKT_NUM) {
			UINT_8 i = 0;
			P_CMD_INFO_T prCmd = &prAdapter->arHifCmdDesc[0];

			DBGLOG(NIC, INFO, "There maybe some cmd info were leaked, free %u\n",
			       prAdapter->rFreeCmdList.u4NumElem);
			for (i = 0; i <= CFG_TX_MAX_CMD_PKT_NUM - 4; i += 4) {
				DBGLOG(NIC, INFO,
				       "ID:%d, N:%p, S:%p; ID:%d, N:%p, S:%p; ID:%d, N:%p, S:%p; ID:%d, N:%p, S:%p\n",
				       prCmd[i].ucCID, prCmd[i].rQueEntry.prNext, &prCmd[i],
				       prCmd[i + 1].ucCID, prCmd[i + 1].rQueEntry.prNext, &prCmd[i + 1],
				       prCmd[i + 2].ucCID, prCmd[i + 2].rQueEntry.prNext, &prCmd[i + 2],
				       prCmd[i + 3].ucCID, prCmd[i + 3].rQueEntry.prNext, &prCmd[i + 3]);
			}
		}
		DBGLOG(NIC, INFO, "Tc4 number:%d\n", prTc->au2FreeBufferCount[TC4_INDEX]);
		/* glResetTrigger(prAdapter); */
	}

	if (prCmdInfo) {
		DBGLOG(MEM, LOUD, "CMD[0x%p] allocated! LEN[%04u], Rest[%u]\n",
				   prCmdInfo, u4Length, prAdapter->rFreeCmdList.u4NumElem);
	} else {
		DBGLOG(MEM, ERROR, "CMD allocation failed! LEN[%04u], Rest[%u]\n",
				   u4Length, prAdapter->rFreeCmdList.u4NumElem);
	}

	return prCmdInfo;

}				/* end of cmdBufAllocateCmdInfo() */

/*----------------------------------------------------------------------------*/
/*!
* @brief This function is used to free the CMD Packet to the MGMT memory pool.
*
* @param prAdapter  Pointer to the Adapter structure.
* @param prCmdInfo  CMD Packet handler
*
* @return (none)
*/
/*----------------------------------------------------------------------------*/
VOID cmdBufFreeCmdInfo(IN P_ADAPTER_T prAdapter, IN P_CMD_INFO_T prCmdInfo)
{
	KAL_SPIN_LOCK_DECLARATION();

	DEBUGFUNC("cmdBufFreeCmdInfo");

	ASSERT(prAdapter);

	if (prCmdInfo) {
		if (prCmdInfo->pucInfoBuffer) {
			cnmMemFree(prAdapter, prCmdInfo->pucInfoBuffer);
			prCmdInfo->pucInfoBuffer = NULL;
		}

		KAL_ACQUIRE_SPIN_LOCK(prAdapter, SPIN_LOCK_CMD_RESOURCE);
		QUEUE_INSERT_TAIL(&prAdapter->rFreeCmdList, &prCmdInfo->rQueEntry);
		KAL_RELEASE_SPIN_LOCK(prAdapter, SPIN_LOCK_CMD_RESOURCE);
	}

	if (prCmdInfo)
		DBGLOG(MEM, LOUD, "CMD[0x%p] freed! Rest[%u]\n", prCmdInfo, prAdapter->rFreeCmdList.u4NumElem);

}				/* end of cmdBufFreeCmdPacket() */
